package top.xzxsrq.fileviewutils.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import top.xzxsrq.ffmpeg.FFmpegUtils;
import top.xzxsrq.fileviewutils.entity.DownloadTask;
import top.xzxsrq.fileviewutils.entity.DownloadTaskSearch;
import top.xzxsrq.fileviewutils.entity.SysUserWithRole;
import top.xzxsrq.fileviewutils.mapper.DownloadTaskMapper;
import top.xzxsrq.fileviewutils.mapper.SysUserMapper;
import top.xzxsrq.fileviewutils.service.DownloadService;
import top.xzxsrq.utils.StringUtilsZX;
import top.xzxsrq.web.download.CommonD;
import top.xzxsrq.web.download.DownloadManager;
import top.xzxsrq.web.download.DownloadThread;
import top.xzxsrq.web.download.entity.PageInfo;
import top.xzxsrq.web.utils.AjaxResult;
import top.xzxsrq.web.utils.WebUtils;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;

@Service
@Log4j2
public class DownloadServiceImpl extends ServiceImpl<DownloadTaskMapper, DownloadTask> implements DownloadService, CommandLineRunner, DisposableBean {

    @Autowired
    ApplicationContext applicationContext;

    private static Collection<CommonD> downloadInterfaces;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private DownloadTaskMapper downloadTaskMapper;

    @Autowired
    private FileServiceImpl fileService;

    @Value("${ffmpegPath}")
    public void setFFmpegPath(String val) {
        FFmpegUtils.FFMPEG = val;
    }

    private static final ScheduledExecutorService threadPoolExecutor = Executors.newScheduledThreadPool(DownloadManager.THREAD_NUMBER + 1);

    public Collection<CommonD> getDownloadInterfaces() {
        if (downloadInterfaces == null) {
            getDownloadListType();
        }
        return downloadInterfaces;
    }

    @Override
    public synchronized AjaxResult getDownloadListType() {
        downloadInterfaces = applicationContext.getBeansOfType(CommonD.class).values();
        List<Map<String, Object>> collect = downloadInterfaces.stream().map(i -> {
            Map<String, Object> result = new HashMap<>();
            result.put("downloadType", i.getDownloadType());
            result.put("downloadParams", i.webRunFuncParams());
            return result;
        }).collect(Collectors.toList());
        return AjaxResult.success(collect);
    }

    @Override
    public AjaxResult submitDownload(JSONObject jsonObject) {
        String downloadType = jsonObject.getString("downloadType");
        if (StringUtilsZX.isBlank(downloadType)) {
            return AjaxResult.failure("下载类型不能为空");
        }
        String downloadName = jsonObject.getString("downloadName");
        if (StringUtilsZX.isBlank(downloadName)) {
            return AjaxResult.failure("下载名称不能为空");
        }
        // 不在参数里面的字段要移除不然会报错
        jsonObject.remove("downloadType");
        jsonObject.remove("downloadName");
        // 判断是否存在同一个任务
        LambdaQueryWrapper<DownloadTask> downloadTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
        downloadTaskLambdaQueryWrapper.eq(DownloadTask::getDownloadParams, jsonObject.toJSONString());
        DownloadTask downloadTask = downloadTaskMapper.selectOne(downloadTaskLambdaQueryWrapper);
        if (downloadTask == null) {
            for (CommonD downloadInterface : getDownloadInterfaces()) {
                if (downloadInterface.getDownloadType().equals(downloadType)) {
                    // 清楚多余的json数据避免出错
                    List<Map<Object, Object>> maps = downloadInterface.webRunFuncParams();
                    for (Map.Entry<String, Object> stringObjectEntry : jsonObject.entrySet()) {
                        String key = stringObjectEntry.getKey();
                        boolean remove = true;
                        for (Map<Object, Object> map : maps) {
                            Object prop = map.get("prop");
                            if (prop != null && prop.equals(key)) {
                                remove = false;
                                break;
                            }
                        }
                        if (remove) {
                            jsonObject.remove(key);
                        }
                    }
                    break;
                }
            }
            DownloadTask downloadTask1 = new DownloadTask();
            downloadTask1.setDownloadTaskId(WebUtils.uuid64());
            SysUserWithRole user = (SysUserWithRole) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            downloadTask1.setUserId(user.getUserId());
            downloadTask1.setDownloadType(downloadType);
            downloadTask1.setDownloadStatus("等待下载");
            downloadTask1.setDownloadParams(jsonObject.toJSONString());
            downloadTask1.setDownloadName(downloadName);
            downloadTaskMapper.insert(downloadTask1);
            noticeDownload();
            return AjaxResult.success("提交完成");
        } else {
            return AjaxResult.success("任务已经存在");
        }
    }

    @Override
    public AjaxResult getDownloadList(DownloadTaskSearch downloadTaskSearch, int pageNum, int pageSize) {
        Page<DownloadTask> downloadTaskSearchPage = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<DownloadTask> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtilsZX.isNotBlank(downloadTaskSearch.getDownloadType())) {
            queryWrapper.eq(DownloadTask::getDownloadType, downloadTaskSearch.getDownloadType());
        }
        if (StringUtilsZX.isNotBlank(downloadTaskSearch.getDownloadName())) {
            queryWrapper.eq(DownloadTask::getDownloadName, downloadTaskSearch.getDownloadName());
        }
        SysUserWithRole user = (SysUserWithRole) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (!user.isInitAdmin()) {
            queryWrapper.eq(DownloadTask::getUserId, user.getUserId());
        }
        Page<DownloadTask> page = page(downloadTaskSearchPage, queryWrapper);
        return AjaxResult.success(page);
    }

    @Override
    public AjaxResult getDownloadDetails(String downloadId) {
        JSONObject result = new JSONObject();
        DownloadTask byId = getById(downloadId);
        result.put("downloadTask", byId);
        List<PageInfo> downloadTaskInfo = new LinkedList<>();
        synchronized (DownloadManager.downloadList) {
            for (DownloadThread downloadThread : DownloadManager.downloadList) {
                if (downloadThread.getDownloadId().equals(byId.getDownloadTaskId())) {
                    PageInfo pageInfo = downloadThread.getPageInfo();
                    if (pageInfo == null) {
                        pageInfo = new PageInfo();
                        pageInfo.setDownloadPageUrl("任务未执行");
                    }
                    downloadTaskInfo.add(pageInfo);
                }
            }
        }
        result.put("downloadTaskInfo", downloadTaskInfo);
        return AjaxResult.success(result);
    }

    @Override
    public AjaxResult delDownload(String downloadId) {
        // 1. 获取下载任务
        DownloadTask byId = getById(downloadId);
        if (byId == null) {
            return AjaxResult.failure("任务不存在");
        }
        // 2. 检查任务状态
        if ("下载完成".equals(byId.getDownloadStatus())) {
            removeById(downloadId);
            return AjaxResult.success("任务已执行完毕，删除成功");
        }
        DownloadManager.cancel(downloadId);
        // 5. 从数据库中删除任务
        try {
            removeById(downloadId);
        } catch (Exception e) {
            log.error("删除任务时发生错误: {}", e.getMessage(), e);
            return AjaxResult.failure("删除任务时发生错误: " + e.getMessage());
        }
        return AjaxResult.success("删除完成");
    }

    @Override
    public AjaxResult downloadAgain(String downloadId) {
        DownloadTask downloadTask = getById(downloadId);
        downloadTask.setDownloadStatus("等待下载");
        downloadTaskMapper.updateById(downloadTask);
        this.noticeDownload();
        return AjaxResult.success("任务已重新进入队列");
    }

    @Override
    public AjaxResult threadInfo() {
        Map<String,Object> data = new LinkedHashMap<>();
        // 下载线程池当前激活线程数量
        data.put("activeCount",DownloadManager.threadPoolExecutor.getActiveCount());
        synchronized (DownloadManager.downloadList) {
            // 已经创建的线程数量
            data.put("downloadThreads",DownloadManager.downloadList.size());
            List<Map<String, Object>> collect = DownloadManager.downloadList.stream().map(i -> {
                Map<String, Object> data1 = new LinkedHashMap<>();
                data1.put("downloadType", i.getDownloadType());
                data1.put("downloadId", i.getDownloadId());
                data1.put("savePath", i.getSavePath());
                data1.put("downloadSelfId", i.getDownloadSelfId());
                return data1;
            }).collect(Collectors.toList());
            data.put("downloadThreadInfoList",collect);
        }
        return AjaxResult.success(data);
    }

    @Override
    public AjaxResult deleTheard(String threadId) {
        DownloadManager.removeByThreadId(threadId);
        return AjaxResult.success();
    }

    @Override
    public AjaxResult urlParsing(JSONObject jsonObject) {
        String downloadType = jsonObject.getString("downloadType");
        for (CommonD downloadInterface : getDownloadInterfaces()) {
            if(downloadInterface.getDownloadType().equals(downloadType)) {
                JSONObject jsonObject1 = downloadInterface.urlParse(jsonObject);
                if(jsonObject1 == null) {
                    return AjaxResult.failure("未实现url解析");
                }
                if(jsonObject1.isEmpty()) {
                    return AjaxResult.failure("url没有传入");
                }
                return AjaxResult.success(jsonObject1);
            }
        }
        return AjaxResult.failure("解析失败");
    }

    public void noticeDownload() {
        int activeCount = DownloadManager.threadPoolExecutor.getActiveCount();
        log.info("下载线程池当前激活线程数量：{}",activeCount);
        if (activeCount < DownloadManager.THREAD_NUMBER) {
            DownloadTask downloadTask = downloadTaskMapper.getNextItem();
            if (downloadTask == null) {
                if(activeCount == 0) {
                    log.info("所有任务完成，关闭htmlunit");
                    CommonD.close();
                }
                return;
            }
            for (CommonD downloadInterface : getDownloadInterfaces()) {
                if (downloadInterface.getDownloadType().equals(downloadTask.getDownloadType())) {
                    String userBasePath = fileService.getBaseFilePathByUserId(downloadTask.getUserId());
                    // 开始任务变更状态
                    downloadTask.setDownloadStatus("下载中");
                    downloadTaskMapper.updateById(downloadTask);
                    threadPoolExecutor.submit(() -> {
                        try {
                            downloadInterface.webRunFunc(
                                    downloadTask.getDownloadTaskId(),
                                    userBasePath,
                                    JSONObject.parseObject(downloadTask.getDownloadParams())
                            );
                            downloadTask.setDownloadStatus("下载完成");
                            downloadTaskMapper.updateById(downloadTask);
                        } catch (Exception e) {
                            log.error("{}：初始化下载线程错误：{}",downloadTask.getDownloadType(),e.getMessage());
                            try {
                                Thread.sleep(2000);
                            } catch (InterruptedException ignored) {
                            }
                            this.noticeDownload();
                            throw new RuntimeException(e);
                        }
                    });
                    break;
                }
            }
        }
    }

    // 程序启动后执行
    @Override
    public void run(String... args) throws Exception {
        log.info("程序启动，下载任务重新开始");
        // 全部变更到等待下载
        downloadTaskMapper.initDownloadStatus();
        log.info("初始化所有任务完成");
        log.info("注册下载线程完成通知");
        DownloadManager.addFinishedCall((downloadThread)->{
            // 任务完成就继续通知下载 调度下一个下载任务，补充下载线程池
            this.noticeDownload();
        });
        log.info("通知任务下载");
        for (int i = 0; i < DownloadManager.THREAD_NUMBER; i++) {
            this.noticeDownload();
        }
    }

    @Override
    public void destroy() throws Exception {
        log.info("关停所有下载线程");
        for (DownloadThread downloadThread : DownloadManager.downloadList) {
            downloadThread.cancel();
        }
        // 监听销毁
        threadPoolExecutor.shutdown();
        DownloadManager.threadPoolExecutor.shutdown();
        log.info("关停所有下载线程完成，系统退出");
    }
}
